﻿using MVSDK;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace MDVCamera
{
    public class MdvCamera2
    {
        public EventHandler OnOpened;
        public EventHandler OnStreamed;
        public EventHandler OnSnapped;
        public EventHandler OnSaved;

        //public string Name;
        public bool SaveImageO = false;
        public string PathImageO = "";

        public MdvParam Param;
        public string ImageFN { get; private set; }
        private void GetOImageF()
        {
            DateTime dt = DateTime.Now;
            if (Directory.Exists(PathImageO) == false) Directory.CreateDirectory(PathImageO);
            string path = Path.Combine(PathImageO, Param.Name);
            if (Directory.Exists(path) == false) Directory.CreateDirectory(path);

            path = Path.Combine(path, dt.ToString("yyyyMMdd"));
            if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
            ImageFN = Path.Combine(path, dt.ToString("HHmmss"));
        }

        private bool streamed = false;
        public bool IsStreamed
        {
            get { return streamed; }
            set
            {
                if (value)
                    SetTriggerModeCont();
                else
                    SetTriggerModeHard();
                streamed = value;
            }
        }

        private object locker_bitmap = new object();
        private Bitmap _bitmap;
        public Bitmap mBitmap
        {
            get
            {
                lock (locker_bitmap) { return _bitmap; }
            }
            private set
            {
                lock (locker_bitmap) { _bitmap = value; }
            }
        }

        private bool _connected = false;
        public bool Connected { get { return _connected; } private set { _connected = value; } }

        #region properties_camera_readonly
        public uint AnalogGainMin { get; private set; }
        public uint AnalogGainMax { get; private set; }
        public float AnalogGainStep { get; private set; }

        private int _analogGain = 10;
        public int AnalogGain
        {
            get { return _analogGain; }
            set
            {
                SetAnalogGain(value);
            }
        }

        public double ExposureMin { get; private set; }
        public double ExposureMax { get; private set; }
        public double ExposureStep { get; private set; }

        private double _exposure = 100;

        public double Exposure
        {
            get { return _exposure; }
            set
            {
                SetExposureTime(value);
            }
        }
        #endregion properties_camera_readonly

        private int hCamera = 0;
        private CameraSdkStatus SdkStatus;
        public IntPtr Data;
        public MdvCamera2()
        {
            clear();
            storeOnConnectionStatus = new CAMERA_CONNECTION_STATUS_CALLBACK(cb_connection);
            storeOnSnapProc = new CAMERA_SNAP_PROC(cb_snap);
        }

        public bool setMirror(int dir, int mirrorCnt) //0是水平，1是垂直
        {

            if (mirrorCnt == 0) return true;
            if (mirrorCnt == 2)
            {
                SdkStatus = MvApi.CameraSetMirror(hCamera, 0, 1);
                if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                {
                    SdkStatus = MvApi.CameraSetMirror(hCamera, 1, 1);
                    if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                        return true;
                    else return false;

                }
                else return false;
            }

            if (mirrorCnt == 1)
            {
                SdkStatus = MvApi.CameraSetMirror(hCamera, dir, 1);
                if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                    return true;
                else return false;
            }
            return true;
        }

        public bool setRotate(int dir) //（0：不旋转 1:90度 2:180度 3:270度）
        {
            SdkStatus = MvApi.CameraSetRotate(hCamera, dir);
            if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                return true;
            else return false;
        }

        public void TriggerEnabled(bool onff)
        {
            if (Connected == false) return;
            try
            {
                IsStreamed = !onff;
                if (onff)//开启触发
                {
                    SetTriggerModeHard();
                }
                else
                {
                    SetTriggerModeCont();
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

        }

        private CAMERA_CONNECTION_STATUS_CALLBACK storeOnConnectionStatus;//声明一个委托，存储相机连接状态
        private CAMERA_SNAP_PROC storeOnSnapProc;//委托，相机采集相关
        private void clear()
        {
            _connected = false;
            IsStreamed = false;
            hCamera = 0;
            SaveImageO = false;

            AnalogGainMin = 10;
            AnalogGainMax = 63;
            AnalogGainStep = 0.125F;
            _analogGain = 10;

            ExposureMin = 0.0;
            ExposureMax = 0.0;
            ExposureStep = 1.0;
            _exposure = 90000.0;
        }

        public void Close()
        {
            if (hCamera != 0)
            {
                Stop();
                MvApi.CameraUnInit(hCamera);
            }

            if (Data != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(Data);
                Data = IntPtr.Zero;
            }
            clear();
        }

        public bool Open()
        {
            SdkStatus = MvApi.CameraInitEx2(Param.Name, out hCamera);
            if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
            {
                after_open();
                _connected = true;
                System.Diagnostics.Debug.WriteLine("MdvCamera2 OPENED");
            }
            else
            {
                _connected = false;
            }
            //LogUtility.LogHelper.Instance().Error("MdvCamera2 " + Name + " Open " + SdkStatus.ToString());
            return _connected;
        }

        private void after_open()
        {
            tSdkCameraCapbility cap;
            SdkStatus = MvApi.CameraGetCapability(hCamera, out cap);

            if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
            {
                if (cap.sIspCapacity.bMonoSensor != 0)
                    SdkStatus = MvApi.CameraSetIspOutFormat(hCamera, (uint)emImageFormat.CAMERA_MEDIA_TYPE_MONO8);
                //开辟图像缓存区地址
                Data = Marshal.AllocHGlobal(cap.sResolutionRange.iWidthMax * cap.sResolutionRange.iHeightMax * 3 + 1024);
            }

            AnalogGainMin = cap.sExposeDesc.uiAnalogGainMin;
            AnalogGainMax = cap.sExposeDesc.uiAnalogGainMax;
            AnalogGainStep = cap.sExposeDesc.fAnalogGainStep;
            setMirror(Param.MirrorDir, Param.MirrorCnt);
            setRotate(Param.RotateDir);
            GetExposures();
            GetAnalogGain();
            //GetGain();
            Start();

            SetTriggerModeSoft();
			//SetTriggerModeHard();
            setCallbacks();

            //SetStrobeMode(Param.StrobeAuto);
            //SetStrobePolarity(Param.StrobeLow);
            //SetStrobeDelayTime((uint)Param.StrobeDelayTime);
            //CameraSetStrobePulseWidth((uint)Param.StrobePluseWidth);
            // SdkStatus = MvApi.CameraSetMirror(hCamera, 1, 1);
            if (OnOpened != null) OnOpened(this, null);
        }
        private bool setCallbacks()
        {
            try
            {
                SdkStatus = MvApi.CameraSetConnectionStatusCallback(hCamera, storeOnConnectionStatus, IntPtr.Zero);
                CAMERA_SNAP_PROC pCaptureCallOld = null;
                SdkStatus = MvApi.CameraSetCallbackFunction(hCamera, storeOnSnapProc, IntPtr.Zero, ref pCaptureCallOld);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                return false;
            }
            return true;
        }

        private void cb_snap(int hCamera, IntPtr pFrameBuffer, ref tSdkFrameHead pFrameHead, IntPtr pContext)
        {
            OnSnapProc(pFrameBuffer, ref pFrameHead);
        }

        private void cb_connection(int hCamera, uint MSG, uint uParam, IntPtr pContext)
        {
            System.Diagnostics.Debug.WriteLine("cb_connection {0} {1} {2}", Param.Name, MSG, uParam);
        }

        void OnSnapProc(IntPtr pFrameBuffer, ref tSdkFrameHead pFrameHead)
        {
            System.Diagnostics.Debug.WriteLine("MdvCamera2.OnSnapProc {0} X {1} {2} ", pFrameHead.iWidth, pFrameHead.iHeight, Param != null ? Param.Name : "CAME");
            lock (this)
            {
                try
                {
                    SdkStatus = MvApi.CameraImageProcess(hCamera, pFrameBuffer, Data, ref pFrameHead);
                    //MvApi.CameraFlipFrameBuffer(Data, ref pFrameHead, 1);

                    // IsStream，StreamCome事件，预处理图像后（清晰度。亮度，对比度，旋转，透视变换等），
                    //并显示，输出亮度和焦点，
                    lock(locker_bitmap) _bitmap = (Bitmap)MvApi.CSharpImageFromFrame(Data, ref pFrameHead);

                    if (SaveImageO)
                    {
                        GetOImageF();
                        SdkStatus = MvApi.CameraSaveImage(hCamera, ImageFN, Data, ref pFrameHead, (emSdkFileType)1, 85);
                        SaveImageO = false;
                        //ImageFN += ImageFN + ".JPG";
                        if (OnSaved != null) OnSaved(this, null);
                    }


                    if (IsStreamed)
                    {
                        if (OnStreamed != null) OnStreamed(this, null);
                    }
                    else
                    {
                       
                        if (OnSnapped != null) OnSnapped(this, null);
                    }
                    MvApi.CameraClearBuffer(hCamera);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }
        }

        public bool TestConnected()
        {
            SdkStatus = MvApi.CameraConnectTest(hCamera);
            if (SdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                _connected = true;
            else
                _connected = false;
            return _connected;
        }

        #region 设置曝光时间和曝光增益

        public void SetGamma(int iGamma)
        {
            MvApi.CameraSetGamma(hCamera, iGamma);
        }

        public void SetGain(int igain)
        {
            MvApi.CameraSetGain(hCamera, igain, igain, igain);
        }

        public int GainR = 0, GainG = 0, GainB = 0;
        public void GetGain()
        {
            MvApi.CameraGetGain(hCamera, ref GainR, ref GainG, ref GainB);
        }

        private void GetAnalogGain()
        {
            MvApi.CameraGetAnalogGain(hCamera, ref _analogGain);
        }

        private void SetAnalogGain(int again)
        {
            if (again < AnalogGainMin || again > AnalogGainMax) return;
            MvApi.CameraSetAnalogGain(hCamera, again);
            int _again = 0;
            if (MvApi.CameraGetAnalogGain(hCamera, ref _again) == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
            {
                _analogGain = _again;
            }
        }

        private void GetExposures()
        {
            double _ExposureMin = 0.0;
            double _ExposureMax = 0.0;
            double _ExposureStep = 0.0;
            if (CameraSdkStatus.CAMERA_STATUS_SUCCESS == MvApi.CameraGetExposureTimeRange(hCamera,
                ref _ExposureMin, ref _ExposureMax, ref _ExposureStep))
            {
                ExposureMin = _ExposureMin * 0.001;
                ExposureMax = _ExposureMax * 0.001;
                ExposureStep = _ExposureStep * 0.001;
                if (MvApi.CameraGetExposureTime(hCamera, ref _exposure) == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                    _exposure = _exposure * 0.001;
            }
        }

        public void SetExposureTime(double fExposureTime)
        {
            MvApi.CameraSetExposureTime(hCamera, fExposureTime * 1000);
            if (MvApi.CameraGetExposureTime(hCamera, ref _exposure) == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                _exposure = _exposure * 0.001;
        }

        public void SaveParameter()
        {
            MvApi.CameraSaveParameter(hCamera, (int)emSdkParameterTeam.PARAMETER_TEAM_A);
        }

        #endregion

        #region TriggerMode

        public int SetTriggerModeCont()
        {
            MvApi.CameraSetOutPutIOMode(hCamera, 0, (int)CameraGPIOMode.IOMODE_GP_OUTPUT);
            MvApi.CameraSetIOStateEx(hCamera, 0, 1);
            SdkStatus = MvApi.CameraSetTriggerMode(hCamera, 0); return (int)SdkStatus;
        }
        public int SetTriggerModeSoft()
        {
            MvApi.CameraSetOutPutIOMode(hCamera, 0, (int)CameraGPIOMode.IOMODE_STROBE_OUTPUT);
            if (CameraSdkStatus.CAMERA_STATUS_SUCCESS == (SdkStatus = MvApi.CameraSetTriggerMode(hCamera, 1)))
            {
                SdkStatus = MvApi.CameraSetTriggerCount(hCamera, 1);
            }
            return (int)SdkStatus;
        }

        public int SetTriggerModeHard()
        {
            MvApi.CameraSetOutPutIOMode(hCamera, 0, (int)CameraGPIOMode.IOMODE_STROBE_OUTPUT);
            if (CameraSdkStatus.CAMERA_STATUS_SUCCESS == (SdkStatus = MvApi.CameraSetTriggerMode(hCamera, 2)))
            {
                SdkStatus = MvApi.CameraSetTriggerCount(hCamera, 1);
            }
            return (int)SdkStatus;
        }

        public int SetStrobeMode(int mode)
        {
            var SdkStatus = MvApi.CameraSetStrobeMode(hCamera, mode);
            return (int)SdkStatus;
        }

        public int GetStrobeMode()
        {
            int val = 0;
            MvApi.CameraGetStrobeMode(hCamera, ref val);
            return val;
        }

        public int SetStrobePolarity(int mode)
        {
            int val = 0;
            MvApi.CameraGetStrobeMode(hCamera, ref val);
            if (val != 0)
            {
                var SdkStatus = MvApi.CameraSetStrobePolarity(hCamera, mode);
                return (int)SdkStatus;
            }
            return 0;
        }

        public int SetStrobeDelayTime(uint mode)
        {
            int val = 0;
            MvApi.CameraGetStrobeMode(hCamera, ref val);
            if (val != 0)
            {
                var SdkStatus = MvApi.CameraSetStrobeDelayTime(hCamera, mode);
                return (int)SdkStatus;
            }
            return 0;
        }

        public int CameraSetStrobePulseWidth(uint mode)
        {
            int val = 0;
            MvApi.CameraGetStrobeMode(hCamera, ref val);
            if(val!=0)
            {
                var SdkStatus = MvApi.CameraSetStrobePulseWidth(hCamera, mode);
                return (int)SdkStatus;
            }
            return 0;
        }
        #endregion TriggerMode

        #region Ops

        public int Start()
        {
            SdkStatus = MvApi.CameraPlay(hCamera); return (int)SdkStatus;
        }

        int Stop()
        {
            SdkStatus = MvApi.CameraStop(hCamera); return (int)SdkStatus;
        }

        public int Pause()
        {
            SdkStatus = MvApi.CameraPause(hCamera); return (int)SdkStatus;
        }

        public int Trigger()
        {
            SdkStatus = MvApi.CameraSoftTriggerEx(hCamera, (uint)0x1);
            return (int)SdkStatus;
        }
        #endregion Ops

    }
}
